package com.example.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.concurrent.locks.ReentrantReadWriteLock;

@Service
@Slf4j
public class DeadlockExampleService {

    private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    private int sharedResource = 0;

    /**
     * 线程1执行的任务
     * 该任务演示了死锁的发生：
     * - 线程1先获取读锁
     * - 在线程1持有读锁的同时，尝试获取写锁，这就导致了锁的升级问题，造成死锁
     */
    public void readAndWriteTask() {
        log.info("线程1: 尝试获取读锁...");
        rwLock.readLock().lock();  // 获取读锁
        try {
            log.info("线程1: 读取共享资源: {}", sharedResource);

            // 模拟一些操作时间
            Thread.sleep(1000);

            log.info("线程1: 尝试获取写锁...");
            // 尝试在持有读锁的情况下获取写锁，这会导致死锁
            rwLock.writeLock().lock();
            try {
                log.info("线程1: 成功获取写锁，更新共享资源");
                sharedResource++;
            } finally {
                rwLock.writeLock().unlock();  // 释放写锁
            }

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("线程1: 任务中断", e);
        } finally {
            rwLock.readLock().unlock();  // 释放读锁
            log.info("线程1: 释放读锁");
        }
    }

    /**
     * 线程2执行的任务
     * - 该任务演示了写锁的获取操作，当另一个线程持有读锁并尝试获取写锁时，可能导致死锁
     */
    public void writeTask() {
        log.info("线程2: 尝试获取写锁...");
        rwLock.writeLock().lock();  // 线程2尝试获取写锁
        try {
            log.info("线程2: 更新共享资源");
            sharedResource++;
        } finally {
            rwLock.writeLock().unlock();  // 释放写锁
            log.info("线程2: 释放写锁");
        }
    }

    /**
     * 模拟死锁的场景
     * - 线程1持有读锁并尝试获取写锁
     * - 线程2获取写锁，导致线程1无法升级锁，产生死锁
     *
     * 具体分析：
     * 线程1持有了读锁，它现在有权读取共享资源。
     * 线程1尝试获取写锁，但写锁需要所有读锁都被释放才能成功获取。由于写锁是独占的（exclusive），这意味着线程1在持有读锁时无法获取写锁，它因此被阻塞。
     * 线程1由于被阻塞在获取写锁的操作上，导致它无法继续执行，无法到达释放读锁的那一步。
     * 线程2尝试获取写锁，但线程2必须等待所有的读锁（包括线程1的读锁）被释放之后，才能获取到写锁。
     *
     *
     * 核心问题在于线程1尝试获取写锁时，陷入阻塞状态，导致无法释放它原先持有的读锁。这并不是典型的“互相等待”的死锁，而是一个单线程陷入阻塞的情况，形成了自我阻塞（Self-blocking）的场景。
     */
    public void simulateDeadlock() {
        log.info("模拟死锁场景开始...");

        // 线程1：获取读锁并尝试获取写锁
        Thread thread1 = new Thread(() -> {
            log.info("线程1: 尝试获取读锁...");
            rwLock.readLock().lock();
            log.info("线程1: 获取了读锁，尝试获取写锁...");
            try {
                Thread.sleep(100);  // 模拟一些操作时间
                log.info("线程1: 尝试获取写锁...");
                rwLock.writeLock().lock();  // 尝试升级为写锁，可能导致死锁
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.error("线程1: 任务中断", e);
            } finally {
                rwLock.readLock().unlock();
                log.info("线程1: 释放读锁");
            }
        });

        // 线程2：获取写锁
        Thread thread2 = new Thread(() -> {
            log.info("线程2: 尝试获取写锁...");
            rwLock.writeLock().lock();
            log.info("线程2: 获取了写锁...");
            try {
                Thread.sleep(100);  // 模拟一些操作时间
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.error("线程2: 任务中断", e);
            } finally {
                rwLock.writeLock().unlock();
                log.info("线程2: 释放写锁");
            }
        });

        thread1.start();
        thread2.start();
    }
}
